<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="LightVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    attribute vec3 a_Normal;
    attribute vec4 a_Tangent;
    attribute vec2 a_Pin;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    varying vec3 v_Normal;
    varying vec3 v_Position;
    varying vec2 v_Pin;
    varying vec4 v_Tangent;

    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
        v_Normal = a_Normal;
        v_Tangent = a_Tangent;
        v_Position = vec3(a_Position);
        v_Pin = a_Pin;
    }
</script>
<script id="LightFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform float u_Kd;//漫反射系数
    uniform float u_Ks;//高光反射系数
    uniform float u_Ka;//环境反射系数

    uniform vec3 u_LightDir;//平行光
    uniform sampler2D u_Sampler;//法线贴图
    uniform vec3 u_KsColor;//高光颜色

    uniform vec3 u_Eye;//视点
    varying vec3 v_Position;//位置

    varying vec3 v_Normal;//法线
    varying vec2 v_Pin;
    void main(){
        vec3 normal = normalize(v_Normal);//将顶点法线归一化(变成单位向量)就是逐片源作色

        vec3 eyeDir = normalize(u_Eye - v_Position);
        vec3 el = eyeDir + u_LightDir;
        vec3 h = el / length(el);

        vec3 color = texture2D(u_Sampler, v_Pin).xyz;

        //漫反射
        vec3 Ld = u_Kd * color * max(0.0, dot(normal, u_LightDir));
        //镜面反射
        vec3 Ls = u_Ks * u_KsColor * pow(max(0.0, dot(normal, h)), 64.0);
        //环境反射
        vec3 La = u_Ka * color;

        //Blinn-Phong反射
        vec3 l = Ld + Ls + La;

        gl_FragColor = vec4(l, 1.0);
    }
</script>
<script id="LightFS2" type="x-shader/x-fragment">
    precision mediump float;
    uniform float u_Kd;//漫反射系数
    uniform float u_Ks;//高光反射系数
    uniform float u_Ka;//环境反射系数

    uniform vec3 u_LightDir;//平行光
    uniform sampler2D u_Sampler;
    uniform sampler2D u_NormalSampler;//法线贴图
    uniform vec3 u_KsColor;//高光颜色

    uniform vec3 u_Eye;//视点
    varying vec3 v_Position;//位置

    varying vec3 v_Normal;//法线
    varying vec2 v_Pin;
    void main(){
        
        float a = 1.0;
        vec2 uv = vec2(v_Pin.x * a, v_Pin.y * a);

        //将顶点法线归一化(变成单位向量)就是逐片源作色
        //但是这样法线会丢失方向
        vec3 normal = normalize(texture2D(u_NormalSampler, uv).xyz + v_Position);

        vec3 eyeDir = normalize(u_Eye - v_Position);
        vec3 el = eyeDir + u_LightDir;
        vec3 h = el / length(el);

        vec3 color = texture2D(u_Sampler, uv).xyz;

        //漫反射
        vec3 Ld = u_Kd * color * max(0.0, dot(normal, u_LightDir));
        //镜面反射
        vec3 Ls = u_Ks * u_KsColor * pow(max(0.0, dot(normal, h)), 64.0);
        //环境反射
        vec3 La = u_Ka * vec3(1.0);

        //Blinn-Phong反射
        vec3 l = Ld + Ls + La;

        gl_FragColor = vec4(l, 1.0);

    }
</script>
<script id="LightFS3" type="x-shader/x-fragment">
    precision mediump float;
    uniform float u_Kd;//漫反射系数
    uniform float u_Ks;//高光反射系数
    uniform float u_Ka;//环境反射系数

    uniform vec3 u_LightDir;//平行光
    uniform sampler2D u_Sampler;
    uniform sampler2D u_NormalSampler;//法线贴图
    uniform vec3 u_KsColor;//高光颜色

    uniform vec3 u_Eye;//视点
    varying vec3 v_Position;//位置

    varying vec3 v_Normal;//法线
    varying vec4 v_Tangent;//切线
    varying vec2 v_Pin;
    void main(){
        
        float a = 1.0;
        vec2 uv = vec2(v_Pin.x * a, v_Pin.y * a);

        //将顶点法线归一化(变成单位向量)就是逐片源作色
        //但是这样法线会丢失方向
        vec3 bumpColor = texture2D(u_NormalSampler, uv).xyz;
        vec3 normal;
        normal.xy = (bumpColor.xy * 2.0 - 1.0); //转换为切线坐标系 [0,1] -> [-1,1]
        normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); //为什么z轴这样算, 意义是什么
        //normal.z = (bumpColor.z * 2.0 - 1.0);

        vec3 binormal = cross(v_Normal.xyz, v_Tangent.xyz) * v_Tangent.w;
        mat3 tangentM = mat3(
            v_Tangent.x, v_Tangent.y, v_Tangent.z,
            binormal.x, binormal.y, binormal.z,
            v_Normal.x, v_Normal.y, v_Normal.z
        ); //这个切线矩阵没搞懂
        normal = normalize(tangentM * normal); //再把切线空间变换到世界坐标

        vec3 eyeDir = 1.0 - normalize(u_Eye - v_Position);
        vec3 el = eyeDir + u_LightDir;
        vec3 h = el / length(el);

        vec3 color = texture2D(u_Sampler, uv).xyz;

        //漫反射
        vec3 Ld = u_Kd * color * max(0.0, dot(normal, u_LightDir));
        //镜面反射
        vec3 Ls = u_Ks * u_KsColor * pow(max(0.0, dot(normal, h)), 64.0);
        //环境反射
        vec3 La = u_Ka * vec3(1.0);

        //Blinn-Phong反射
        vec3 l = Ld + Ls + La;

        gl_FragColor = vec4(l, 1.0);
    }
</script>
<script id="LightFS4" type="x-shader/x-fragment">
    precision mediump float;
    uniform float u_Kd;//漫反射系数
    uniform float u_Ks;//高光反射系数
    uniform float u_Ka;//环境反射系数

    uniform vec3 u_LightDir;//平行光
    uniform sampler2D u_Sampler;
    uniform sampler2D u_NormalSampler;//法线贴图
    uniform vec3 u_KsColor;//高光颜色

    uniform vec3 u_Eye;//视点
    varying vec3 v_Position;//位置

    varying vec3 v_Normal;//法线
    varying vec4 v_Tangent;//切线
    varying vec2 v_Pin;
    void main(){
        
        float a = 1.0;
        vec2 uv = vec2(v_Pin.x * a, v_Pin.y * a);

        //将顶点法线归一化(变成单位向量)就是逐片源作色
        //但是这样法线会丢失方向
        vec3 bumpColor = texture2D(u_NormalSampler, uv).xyz;
        vec3 normal;
        normal.xy = (bumpColor.xy * 2.0 - 1.0); //转换为切线坐标系 [0,1] -> [-1,1]
        //normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); //为什么z轴这样算, 意义是什么
        normal.z = (bumpColor.z * 2.0 - 1.0);

        vec3 binormal = cross(v_Normal.xyz, v_Tangent.xyz) * v_Tangent.w;
        mat3 tangentM = mat3(
            v_Tangent.x, v_Tangent.y, v_Tangent.z,
            binormal.x, binormal.y, binormal.z,
            v_Normal.x, v_Normal.y, v_Normal.z
        ); //这个切线矩阵没搞懂
        normal = normalize(tangentM * normal); //再把切线空间变换到世界坐标

        vec3 eyeDir = 1.0 - normalize(u_Eye - v_Position);
        vec3 el = eyeDir + u_LightDir;
        vec3 h = el / length(el);

        vec3 color = texture2D(u_Sampler, uv).xyz;

        //漫反射
        vec3 Ld = u_Kd * color * max(0.0, dot(normal, u_LightDir));
        //镜面反射
        vec3 Ls = u_Ks * u_KsColor * pow(max(0.0, dot(normal, h)), 64.0);
        //环境反射
        vec3 La = u_Ka * vec3(1.0);

        //Blinn-Phong反射
        vec3 l = Ld + Ls + La;

        gl_FragColor = vec4(l, 1.0);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, OrthographicCamera, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Poly from '../js/Poly4.js'
    import Control from '../js/Control.js'
    import Earth from '../js/objs/Earth.js'
    import Box from '../js/objs/Box.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const gl = canvas.getContext("webgl", { antialias: true, });
    console.log(gl)
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    gl.enable(gl.DEPTH_TEST);
    //#endregion

    const scene = new Scene({ gl: gl });
    scene.addAxisProgram();
    scene.regProgram('light1', {
        program: CreateProgram(gl,
            document.querySelector('#LightVS').innerText,
            document.querySelector('#LightFS').innerText),
        attribs: ['a_Position', 'a_Normal', 'a_Pin'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_PointSize', 'u_Sampler', 'u_NormalSampler', 'u_KsColor',
            'u_LightDir', 'u_Kd', 'u_Ks', 'u_Ka', 'u_Eye']
    });
    scene.regProgram('light3', {
        program: CreateProgram(gl,
            document.querySelector('#LightVS').innerText,
            document.querySelector('#LightFS3').innerText),
        attribs: ['a_Position', 'a_Normal', 'a_Pin', 'a_Tangent'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_PointSize', 'u_Sampler', 'u_NormalSampler', 'u_KsColor',
            'u_LightDir', 'u_Kd', 'u_Ks', 'u_Ka', 'u_Eye']
    });
    scene.regProgram('light4', {
        program: CreateProgram(gl,
            document.querySelector('#LightVS').innerText,
            document.querySelector('#LightFS4').innerText),
        attribs: ['a_Position', 'a_Normal', 'a_Pin', 'a_Tangent'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_PointSize', 'u_Sampler', 'u_NormalSampler', 'u_KsColor',
            'u_LightDir', 'u_Kd', 'u_Ks', 'u_Ka', 'u_Eye']
    });


    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4();
    const eye = new Vector3(0, 5, 0);
    const lookAt = new Vector3();

    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 1, 20);
    const cameraController = new CameraController({
        canvas,
        camera,
        pvMatrix: pvM4,
        eye,
        lookAt,
    });


    const axis = new Obj3D().createAxis('axis');
    scene.add(axis);

    const earth = new Earth(0.5);
    const box = new Box(0.5);
    // const [ver, ele, nor, uv] = [
    //     earth.vertices, earth.elements, earth.normals, earth.uv
    // ];
    const [ver, ele, nor, uv, tan] = [
        box.vertices, box.elements, box.normals, box.uv, box.tangent,
    ];
    // const [ver, ele, nor, uv] = [
    //     // [-1, 0, -1, -1, 0, 1, 1, 0, 1, 1, 0, -1],
    //     [-1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0],
    //     [0, 1, 2, 0, 2, 3], [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], [0, 1, 0, 0, 1, 0, 1, 1]
    // ];

    const LightDir = new Vector3(0, 5, 0).normalize();//光线方向
    const Kd = 0.7;//漫反射系数
    const Ks = 0.9;//镜面反射系数
    const Ka = 0.1;//环境反射系数



    //#region main
    //这里是直接读取 法线贴图的值生成的新法线 并不是在切线坐标系中扰动法线

    const image = new Image();
    image.src = '../Image/normal01.jpg';
    image.onload = function () {

        const image01 = new Image();
        image01.src = '../Image/Image01.jpg';
        image01.onload = function () {

            let obj01 = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(ver) },
                        'a_Normal': { size: 3, array: new Float32Array(nor) },
                        'a_Tangent': { size: 3, array: new Float32Array(tan) },
                        'a_Pin': { size: 2, array: new Float32Array(uv) },
                    },
                    element: { array: new Uint16Array(ele) }
                }),
                mat: new Mat({
                    program: 'light3',
                    mode: ['TRIANGLES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().setPosition(-1.0, 0, 0).elements },
                        'u_Kd': { type: 'uniform1f', value: Kd },
                        'u_Ks': { type: 'uniform1f', value: Ks },
                        'u_Ka': { type: 'uniform1f', value: Ka },
                        'u_LightDir': { type: 'uniform3fv', value: [...LightDir] },
                        'u_Eye': { type: 'uniform3fv', value: [...cameraController.eye] },
                        'u_KsColor': { type: 'uniform3fv', value: [1, 1, 1] },
                    },
                    maps: {
                        'u_Sampler': {
                            image: image01,
                            WRAP_W: gl.CLAMP_TO_EDGE,
                            WRAP_H: gl.CLAMP_TO_EDGE,
                            MIN_FILTER: gl.LINEAR,
                        },
                        'u_NormalSampler': {
                            image: image,
                            WRAP_W: gl.CLAMP_TO_EDGE,
                            WRAP_H: gl.CLAMP_TO_EDGE,
                            MIN_FILTER: gl.LINEAR,
                        },
                    }
                }),
            });
            scene.add(obj01);

            let obj02 = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(ver) },
                        'a_Normal': { size: 3, array: new Float32Array(nor) },
                        'a_Tangent': { size: 3, array: new Float32Array(tan) },
                        'a_Pin': { size: 2, array: new Float32Array(uv) },
                    },
                    element: { array: new Uint16Array(ele) }
                }),
                mat: new Mat({
                    program: 'light4',
                    mode: ['TRIANGLES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().setPosition(1.0, 0, 0).elements },
                        'u_Kd': { type: 'uniform1f', value: Kd },
                        'u_Ks': { type: 'uniform1f', value: Ks },
                        'u_Ka': { type: 'uniform1f', value: Ka },
                        'u_LightDir': { type: 'uniform3fv', value: [...LightDir] },
                        'u_Eye': { type: 'uniform3fv', value: [...cameraController.eye] },
                        'u_KsColor': { type: 'uniform3fv', value: [1, 1, 1] },
                    },
                    maps: {
                        'u_Sampler': {
                            image: image01,
                            WRAP_W: gl.CLAMP_TO_EDGE,
                            WRAP_H: gl.CLAMP_TO_EDGE,
                            MIN_FILTER: gl.LINEAR,
                        },
                        'u_NormalSampler': {
                            image: image,
                            WRAP_W: gl.CLAMP_TO_EDGE,
                            WRAP_H: gl.CLAMP_TO_EDGE,
                            MIN_FILTER: gl.LINEAR,
                        },
                    }
                }),
            });
            scene.add(obj02);

        }
    };


    //#endregion



    (function ani() {
        draw();
        requestAnimationFrame(ani)
    })();



    function draw() {
        scene.setUniform('u_PvM4', { value: pvM4.elements });
        scene.setUniform('u_Eye', { value: [...cameraController.eye] });
        // scene.setUniform('u_LightDir', { value: [...cameraController.eye] });
        scene.draw();
    }
</script>

</html>